package model.collisions;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import model.collisions.effects.BulletDie;
import model.collisions.effects.CollisionEffect;
import model.collisions.effects.ModifySpeed;
import model.collisions.effects.ModifyTTL;
import model.collisions.effects.ModifyWeapon;
import model.collisions.effects.StopAndDie;
import model.collisions.effects.TakeDamage;
import model.collisions.effects.YouShallNotPass;

/**
 * El diccionacrio de Colisiones.
 * Un HashMap que contiene todas las entradas que debe usar el engine de colisiones, para calcular los resultados de una colision.
 * Asocia un par de categorias a un efecto.
 * Las entradas se dan del Elemento que colisiona hacia el elemento contra el que colisiono, y nunca en el sentido contrario.
 * Los efectos se aplican desde el colisionado hacia el colisionador.
 */
abstract class CollisionDictionary
{
	
	/**
	 * Mapa que contiene el diccionario de effectos
	 */
	private static Map<CollisionDictionaryKey,ArrayList<CollisionEffect>> m_dictionary = 
				new HashMap<CollisionDictionaryKey,ArrayList<CollisionEffect>>();
	
	/**
	 * Lista de punteros a effectos que se llaman para cada combinacion de effectos
	 */
	private static CollisionEffect m_youShallNotPass = new YouShallNotPass();
	private static CollisionEffect m_bulletDie 		 = new BulletDie(); 
	private static CollisionEffect m_modifySpeed	 = new ModifySpeed(); 
	private static CollisionEffect m_modifyTTL		 = new ModifyTTL(); 
	private static CollisionEffect m_modifyWeapon	 = new ModifyWeapon(); 
	private static CollisionEffect m_stopAndDie		 = new StopAndDie(); 
	private static CollisionEffect m_takeDamage		 = new TakeDamage();  
	
	static
	{		
		/*
		 * Se agregan los efectos al diccionario una unica vez
		 */

		// Colisiones que detienen
		ArrayList<CollisionEffect> entryList = new ArrayList<CollisionEffect>();
		entryList.add(m_youShallNotPass);
		m_dictionary.put( new CollisionDictionaryKey(CollisionCategory.C_PLAYER, CollisionCategory.C_T1_WALL), 	entryList );
		m_dictionary.put( new CollisionDictionaryKey(CollisionCategory.C_PLAYER, CollisionCategory.C_T2_WALL), 	entryList );
		m_dictionary.put( new CollisionDictionaryKey(CollisionCategory.C_PLAYER, CollisionCategory.C_T3_WALL), 	entryList );
		m_dictionary.put( new CollisionDictionaryKey(CollisionCategory.C_NPC, CollisionCategory.C_T1_WALL), 	entryList );
		m_dictionary.put( new CollisionDictionaryKey(CollisionCategory.C_NPC, CollisionCategory.C_T2_WALL), 	entryList );
		m_dictionary.put( new CollisionDictionaryKey(CollisionCategory.C_NPC, CollisionCategory.C_T3_WALL), 	entryList );
		m_dictionary.put( new CollisionDictionaryKey(CollisionCategory.C_NPC, CollisionCategory.C_NPC), 		entryList );
		
		for (CollisionCategory cat : CollisionCategory.values())
		{
			m_dictionary.put( new CollisionDictionaryKey(cat, CollisionCategory.C_IMPASSABLE), 	entryList );
		}		
		
		// Colisiones que detienen que hacen daño
		entryList = new ArrayList<CollisionEffect>();
		entryList.add(m_takeDamage);
		m_dictionary.put( new CollisionDictionaryKey(CollisionCategory.C_PLAYER, CollisionCategory.C_NPC), 				entryList );
		m_dictionary.put( new CollisionDictionaryKey(CollisionCategory.C_PLAYER, CollisionCategory.C_GHOST), 			entryList );
		m_dictionary.put( new CollisionDictionaryKey(CollisionCategory.C_PLAYER, CollisionCategory.C_BULLET), 			entryList );		
		m_dictionary.put( new CollisionDictionaryKey(CollisionCategory.C_PLAYER, CollisionCategory.C_L1_EXPLOTION), 	entryList );
		m_dictionary.put( new CollisionDictionaryKey(CollisionCategory.C_NPC, CollisionCategory.C_L1_EXPLOTION), 		entryList );
		m_dictionary.put( new CollisionDictionaryKey(CollisionCategory.C_GHOST, CollisionCategory.C_L1_EXPLOTION), 		entryList );        
		m_dictionary.put( new CollisionDictionaryKey(CollisionCategory.C_T1_WALL, CollisionCategory.C_L1_EXPLOTION), 	entryList );		
		m_dictionary.put( new CollisionDictionaryKey(CollisionCategory.C_PLAYER, CollisionCategory.C_L2_EXPLOTION), 	entryList );
		m_dictionary.put( new CollisionDictionaryKey(CollisionCategory.C_NPC, CollisionCategory.C_L2_EXPLOTION), 		entryList );
		m_dictionary.put( new CollisionDictionaryKey(CollisionCategory.C_GHOST, CollisionCategory.C_L2_EXPLOTION), 		entryList );        
		m_dictionary.put( new CollisionDictionaryKey(CollisionCategory.C_T1_WALL, CollisionCategory.C_L2_EXPLOTION), 	entryList );        
		m_dictionary.put( new CollisionDictionaryKey(CollisionCategory.C_T2_WALL, CollisionCategory.C_L2_EXPLOTION), 	entryList );  
		
		// Colisiones que modifican la velocidad
		entryList = new ArrayList<CollisionEffect>();
		entryList.add(m_modifySpeed);
		m_dictionary.put( new CollisionDictionaryKey(CollisionCategory.C_PLAYER, CollisionCategory.C_MODIFY_SPEED), entryList );
	
		// Colisiones que modifican el TTL del arma
		entryList = new ArrayList<CollisionEffect>();
		entryList.add(m_modifyTTL);
		m_dictionary.put( new CollisionDictionaryKey(CollisionCategory.C_PLAYER, CollisionCategory.C_MODIFY_TTL), 	entryList );
		
		// Colisiones que modifican el TTL del arma
		entryList = new ArrayList<CollisionEffect>();
		entryList.add(m_modifyWeapon);
		m_dictionary.put( new CollisionDictionaryKey(CollisionCategory.C_PLAYER, CollisionCategory.C_MODIFY_WEAPON), entryList );
		
		// Effectos de Explosiones
		entryList = new ArrayList<CollisionEffect>();
		entryList.add(m_stopAndDie);
		m_dictionary.put( new CollisionDictionaryKey(CollisionCategory.C_L1_EXPLOTION, CollisionCategory.C_T1_WALL), entryList );
		m_dictionary.put( new CollisionDictionaryKey(CollisionCategory.C_L1_EXPLOTION, CollisionCategory.C_T2_WALL), entryList );
		m_dictionary.put( new CollisionDictionaryKey(CollisionCategory.C_L1_EXPLOTION, CollisionCategory.C_T3_WALL), entryList );
		m_dictionary.put( new CollisionDictionaryKey(CollisionCategory.C_L2_EXPLOTION, CollisionCategory.C_T1_WALL), entryList );
		m_dictionary.put( new CollisionDictionaryKey(CollisionCategory.C_L2_EXPLOTION, CollisionCategory.C_T2_WALL), entryList );
		m_dictionary.put( new CollisionDictionaryKey(CollisionCategory.C_L2_EXPLOTION, CollisionCategory.C_T3_WALL), entryList );
	    
		// Effectos de las balas al colisionar contra cosas
		entryList = new ArrayList<CollisionEffect>();
		entryList.add(m_bulletDie);
		for (CollisionCategory cat : CollisionCategory.values())
		{
			if(cat != CollisionCategory.C_NO_EFFECT)
			{
				m_dictionary.put( new CollisionDictionaryKey(CollisionCategory.C_BULLET, cat), 	entryList );
			}
		}		
		
	}

	/**
	 * Obtiene la lista de efectos del diccionario, para un par de categorias de colision.
	 *
	 * @param collisioner el elemento que colisiona
	 * @param collisionee el elemento contra el que colisiono
	 * @return Una lista de efectos de colision
	 */
	public static List<CollisionEffect> getEffectsFor( final Collisionable collisioner, final Collisionable collisionee )
	{
		
		List<CollisionEffect> returnList = m_dictionary.get
        (
			new CollisionDictionaryKey
			(
				collisioner.getCollisionCategory(), 
				collisionee.getCollisionCategory()
			)
		);
		
		//Si no encontro resultados, devuelve una lista vacia
		if(returnList == null)
		{
			returnList = new ArrayList<CollisionEffect>();
		}
		
		return returnList;
		
	}

}
